// (c) Ivan Gagis 2008
// e-mail: igagis@gmail.com
// Version: 0.1

// Description:
//          cross platfrom C++ Sockets wrapper
//
//      Some code was taken from SDL_net library (http://libsdl.org),
//      so, this wrapper inherits the LGPL license from SDL_net.
//

#ifndef M_C_SOCKETS_HPP
#define M_C_SOCKETS_HPP

#include <exception>

namespace sckt{

//some typedefs, these are system specific, so maybe there will appear some #ifdef s in the future, for different systems.
typedef unsigned int uint;
typedef unsigned int u32;
typedef unsigned short u16;
typedef unsigned char byte;

//forward declarations
class C_SocketSet;

class C_Exc : public std::exception{
    char *msg;
public:
    C_Exc(const char* message = 0);
    virtual ~C_Exc()throw();
    
    const char *What()const throw(){
        return this->msg;
    };
    
private:
    //override from std::exception
    const char *what()const throw(){
        return this->What();
    };
};

//singletone
class C_Sockets{
    static C_Sockets *instance;
public:
    C_Sockets();
    ~C_Sockets();
private:
    static void InitSockets();
    static void DeinitSockets();
};

class C_IPAddress{
public:
    u32 host;
    u16 port;
    
    C_IPAddress(){};
    
    C_IPAddress(u32 h, u16 p) :
            host(h),
            port(p)
    {};
    
    //create ip address for listening on the specified port
    C_IPAddress(u16 p);
};

class C_Socket{
    friend class C_TCPSocket;
    friend class C_SocketSet;
    
    bool isReady;
    
public:
    //this type will hold system specific socket handle.
    //this buffer should be large enough to hold socket handle in different systems.
    //sizeof(int) looks enough so far.
    struct C_SystemIndependentSocketHandle{
        sckt::byte buffer[sizeof(int)];
    };
private:
    C_SystemIndependentSocketHandle socket;
    
    C_Socket();
public:
    virtual ~C_Socket(){};
    
    bool IsValid()const;
    
    inline bool IsReady()const{
        return this->isReady;
    };
};

class C_TCPSocket : public C_Socket{
    bool isServer;
public:
    C_TCPSocket() :
            isServer(false)
    {};
    
    //copy constructor
    C_TCPSocket(const C_TCPSocket& s){
        //NOTE: that operator= calls destructor, so this->socket should be invalid, base class constructor takes care about it.
        this->operator=(s);//same as C_Ptr
    };
    
    C_TCPSocket(const C_IPAddress& ip) :
            isServer(false)
    {
        this->Open(ip);
    };
    
    C_TCPSocket& operator=(const C_TCPSocket& s);
    
    ~C_TCPSocket(){
        this->Close();
    };
    
    void Open(const C_IPAddress& ip);
    
    void Close();
    
    uint Send(const byte* data, uint size);
    
    //returns 0 if connection was closed by peer
    uint Recv(byte* buf, uint maxSize);
    
    C_TCPSocket Accept();
};

class C_SocketSet{
    C_Socket** set;
    uint maxSockets;
    uint numSockets;
  public:
    C_SocketSet(uint maxNumSocks) :
            set(new C_Socket*[maxNumSocks]),
            maxSockets(maxNumSocks),
            numSockets(0)
    {};
    
    ~C_SocketSet(){
        delete[] set;
    };
    
    inline uint NumSockets()const{return this->numSockets;};
    inline uint MaxSockets()const{return this->maxSockets;};
    
    void AddSocket(C_Socket *sock);
    
    void RemoveSocket(C_Socket *sock);
    
    //This function checks to see if data is available for reading on the
    //given set of sockets.  If 'timeout' is 0, it performs a quick poll,
    //otherwise the function returns when either data is available for
    //reading, or the timeout in milliseconds has elapsed, which ever occurs
    //first.  This function returns true if there are any sockets ready for reading,
    //or false if there was an error with the select() system call.
    bool CheckSockets(uint timeoutMillis);
};

};//~namespace sckt
#endif//~once
